<template>
	<div class="container">
		<div class="left-board">
			<div class="logo-wrapper"></div>
			<el-scrollbar class="left-scrollbar">
				<div class="components-list">
					<div v-for="(item, listIndex) in leftComponents" :key="listIndex">
						<div class="components-title">
							<svg-icon icon-class="component" />
							{{ item.title }}
						</div>
						<draggable
							class="components-draggable"
							:list="item.list"
							:group="{ name: 'componentsGroup', pull: 'clone', put: false }"
							:clone="cloneComponent"
							draggable=".components-item"
							:sort="false"
							@end="onEnd"
						>
							<div
								v-for="(element, index) in item.list"
								:key="index"
								class="components-item"
								@click="addComponent(element)"
							>
								<div class="components-body">
									<svg-icon :icon-class="element.__config__.tagIcon" />
									{{ element.__config__.label }}
								</div>
							</div>
						</draggable>
					</div>
				</div>
			</el-scrollbar>
		</div>

		<div class="center-board">
			<div class="action-bar">
				<el-button icon="el-icon-video-play" type="text" @click="run">
					运行
				</el-button>
				<!-- <el-button icon="el-icon-video-play" type="text" @click="preview">
          预览
        </el-button> -->
				<el-button type="text" @click="handleSave"> 保存 </el-button>
				<el-button icon="el-icon-view" type="text" @click="showJson">
					查看json
				</el-button>
				<el-button icon="el-icon-download" type="text" @click="download">
					导出文件
				</el-button>
				<el-button
					class="copy-btn-main"
					icon="el-icon-document-copy"
					type="text"
					@click="copy"
				>
					复制代码
				</el-button>
				<el-button
					class="delete-btn"
					icon="el-icon-delete"
					type="text"
					@click="empty"
				>
					清空
				</el-button>
			</div>
			<el-scrollbar class="center-scrollbar">
				<el-row class="center-board-row" :gutter="formConf.gutter">
					<el-form
						:size="formConf.size"
						:label-position="formConf.labelPosition"
						:disabled="formConf.disabled"
						:label-width="formConf.labelWidth + 'px'"
					>
						<draggable
							class="drawing-board"
							:list="drawingList"
							:animation="340"
							group="componentsGroup"
						>
							<draggable-item
								v-for="(item, index) in drawingList"
								:key="item.renderKey"
								:drawing-list="drawingList"
								:current-item="item"
								:index="index"
								:active-id="activeId"
								:form-conf="formConf"
								@activeItem="activeFormItem"
								@copyItem="drawingItemCopy"
								@deleteItem="drawingItemDelete"
							/>
						</draggable>
						<div v-show="!drawingList.length" class="empty-info">
							从左侧拖入或点选组件进行表单设计
						</div>
					</el-form>
				</el-row>
			</el-scrollbar>
		</div>

		<right-panel
			:active-datas="activeData"
			:dict-name-list="dictNameList"
			:form-confs="formConf"
			:show-field="!!drawingList.length"
			@tag-change="tagChange"
			@fetch-data="fetchData"
			@tableHeaderEdit="tableHeaderEdit"
			@tableDataEdit="tableDataEdit"
			@selectEdit="selectEdit"
		/>

		<form-drawer
			:visible.sync="drawerVisible"
			:form-data="formData"
			size="100%"
			:generate-conf="generateConf"
		/>
		<json-drawer
			size="60%"
			:visible.sync="jsonDrawerVisible"
			:json-str="JSON.stringify(formData)"
			@refresh="refreshJson"
		/>
		<code-type-dialog
			:visible.sync="dialogVisible"
			title="选择生成类型"
			:show-file-name="showFileName"
			@confirm="generate"
		/>
		<input id="copyNode" type="hidden" />
	</div>
</template>

<script>
import draggable from 'vuedraggable'
import { debounce } from 'throttle-debounce'
import { saveAs } from 'file-saver'
import ClipboardJS from 'clipboard'
// import render from '@/components/render/render'
import FormDrawer from './FormDrawer'
import JsonDrawer from './JsonDrawer'
import RightPanel from './RightPanel'
// import { formDataSave } from '@/http/api'
import {
	inputComponents,
	selectComponents,
	layoutComponents,
	formConf,
} from '@/components/generator/config'
import {
	beautifierConf,
	titleCase,
	deepClone,
	isObjectObject,
} from '@/utils/index'
import {
	makeUpHtml,
	vueTemplate,
	vueScript,
	cssStyle,
	vueToHtml,
} from '@/components/generator/html'
import { makeUpJs } from '@/components/generator/js'
import { makeUpCss } from '@/components/generator/css'
import drawingDefalut from '@/components/generator/drawingDefalut'
import logo from '@/assets/logo.png'
import CodeTypeDialog from './CodeTypeDialog'
import DraggableItem from './DraggableItem'
import {
	// getDrawingList,
	saveDrawingList,
	getIdGlobal,
	saveIdGlobal,
	// getFormConf,
} from '@/utils/db'
import loadBeautifier from '@/utils/loadBeautifier'
import { mapGetters, mapMutations } from 'vuex'
import { getCustomerFormJson, customerFormUpdate } from '@/http/customerForm'
let beautifier
// const emptyActiveData = { style: {}, autosize: {} }
let oldActiveId
let tempActiveData
// const drawingListInDB = getDrawingList()
// const formConfInDB = getFormConf()
const idGlobal = getIdGlobal()
export default {
	name: 'FormGenerator',
	components: {
		draggable,
		// render,
		FormDrawer,
		JsonDrawer,
		RightPanel,
		CodeTypeDialog,
		DraggableItem,
	},
	data() {
		let drawingDefaluts = this._.cloneDeep(drawingDefalut)
		return {
			logo,
			idGlobal,
			formConf,
			inputComponents,
			selectComponents,
			layoutComponents,
			labelWidth: 100,
			drawingList: drawingDefaluts,
			drawingData: {},
			activeId: drawingDefaluts[0].formId,
			drawerVisible: false,
			formData: {},
			dialogVisible: false,
			jsonDrawerVisible: false,
			generateConf: null,
			showFileName: false,
			activeData: drawingDefaluts[0],
			dictNameList: [],
			saveDrawingListDebounce: debounce(340, saveDrawingList),
			saveIdGlobalDebounce: debounce(340, saveIdGlobal),
			leftComponents: [
				{
					title: '输入型组件',
					list: inputComponents,
				},
				{
					title: '选择型组件',
					list: selectComponents,
				},
				{
					title: '布局型组件',
					list: layoutComponents,
				},
			],
		}
	},
	computed: {
		...mapGetters(['curFormInfo', 'tabsList']),
	},
	watch: {
		// eslint-disable-next-line func-names
		'activeData.__config__.label': function (val, oldVal) {
			if (
				this.activeData.placeholder === undefined ||
				!this.activeData.__config__.tag ||
				oldActiveId !== this.activeId
			) {
				return
			}
			this.activeData.placeholder =
				this.activeData.placeholder.replace(oldVal, '') + val
		},
		activeId: {
			handler(val) {
				oldActiveId = val
			},
			immediate: true,
		},
		drawingList: {
			handler(val) {
				// console.log(val, 33333)
				// this.saveDrawingListDebounce(val)
				if (val.length === 0) this.idGlobal = 100
			},
			deep: true,
			immediate: true,
		},
		idGlobal: {
			handler(val) {
				this.saveIdGlobalDebounce(val)
			},
			immediate: true,
		},
		curFormInfo: {
			handler: function (val) {
				// console.log(val, 5666666)
				if (!val?.id) {
					this.closeCurTab()
				} else {
					this.formConf.formName = val.formName
					this.getCustomerFormJson(val.id)
				}
			},
			immediate: true,
			deep: true,
		},
	},
	async created() {
		loadBeautifier(btf => {
			beautifier = btf
		})
	},
	mounted() {
		const clipboard = new ClipboardJS('#copyNode', {
			text: () => {
				const codeStr = this.generateCode()
				this.$notify({
					title: '成功',
					message: '代码已复制到剪切板，可粘贴。',
					type: 'success',
				})
				return codeStr
			},
		})
		clipboard.on('error', () => {
			this.$message.error('代码复制失败')
		})
		// this.$nextTick(() => {
		// 	this.drawingList = drawingDefalut
		// 	// console.log(this.drawingList, drawingDefalut, 3333)
		// })
	},
	methods: {
		...mapMutations('common', ['CLOSETAB', 'CHANGE_DEFAULTACTIVE']),
		closeCurTab() {
			let item = this.tabsList.find(i => i.path === this.$route.path)
			// console.log(item)
			this.CLOSETAB(item.path)
			let art = this.tabsList[this.tabsList.length - 1]
			if (art.componentName === 'MainView') {
				art = this.tabsList[this.tabsList.length - 2]
			}
			this.$router.push(art.path)
			this.CHANGE_DEFAULTACTIVE(art.path)
		},
		// async getDataByCache() {
		// 	let arr = await getDrawingList(this.curFormInfo?.id)
		// 	if (Array.isArray(arr) && arr.length > 0) {
		// 		this.drawingList = arr
		// 	} else {
		// 		this.drawingList = drawingDefalut
		// 	}
		// 	this.activeFormItem(this.drawingList[0])
		// 	if (await getFormConf(this.curFormInfo?.id)) {
		// 		this.formConf = getFormConf(this.curFormInfo?.id)
		// 	}
		// },
		getCustomerFormJson(id) {
			getCustomerFormJson({ id })
				.then(({ data }) => {
					if (data?.head?.code == 200) {
						// console.log(data.data, 55555)
						this.refreshJson(data.data)
					}
				})
				.catch(({ data }) => {
					// console.log(e)
					if (data?.head?.code != 400) {
						this.$message.error(data?.head?.msg || '网络异常，请联系管理员')
					} else {
						this.formConf.formUrl = ''
						this.$message.info('当前数据为空，请设计表单')
					}
				})
		},
		selectEdit(data, url) {
			this.$set(this.activeData.__slot__, 'options', data)
			this.$set(this.activeData.__config__, 'url', url)
			if (url && url.includes('http')) {
				this.$set(this.activeData.__config__, 'dataType', 'dynamic')
			} else {
				this.$set(this.activeData.__config__, 'dataType', 'static')
			}
		},
		tableHeaderEdit(val, url) {
			this.$set(this.activeData.props, 'columns', val.columns)
			this.$set(this.activeData.props, 'props', val.props)
			this.$set(this.activeData.props, 'filterFields', val.filterFields)
			if (url && url.includes('http')) {
				this.$set(this.activeData.__slot__, 'tableHeaderUrl', url)
			}
		},
		tableDataEdit(val, url) {
			// console.log(val)
			// this.activeData.data = val
			this.$set(this.activeData.props, 'data', val)
			if (url && url.includes('http')) {
				this.$set(this.activeData.__slot__, 'tableDataUrl', url)
			}
		},
		handleSave() {
			if (!this.formConf.formName || !this.formConf.formUrl) {
				this.$message.error('请检查表单名称或表单提交地址')
				return
			}
			const obj = {
				fields: deepClone(this.drawingList),
				...this.formConf,
			}
			obj.fields.forEach(i => {
				if (i.__config__.tag == 'table-select') {
					if (i.__slot__.tableHeaderUrl) {
						i.props.filterFields = []
						i.props.columns = []
						i.props.props = {}
					}
					if (i.__slot__.tableDataUrl) {
						i.props.data = []
					}
				} else if (
					(i.__config__.tag == 'el-select' ||
						i.__config__.tag == 'el-cascader') &&
					i.__config__.url
				) {
					if (i.__slot__) {
						i.__slot__.options = []
					}
					if (i.options) {
						i.options = []
					}
				}
			})
			let res = this._.cloneDeep(this.curFormInfo)
			res.formJson = JSON.stringify(obj)
			customerFormUpdate(res).then(({ data }) => {
				// console.log(data)
				if (data?.head?.code == 200) {
					// this.closeCurTab()
					this.$confirm('保存成功，是否关闭当前页面?', '提示', {
						confirmButtonText: '确定',
						cancelButtonText: '取消',
						type: 'success',
					})
						.then(() => {
							this.closeCurTab()
						})
						.catch(() => {})
				}
			})
		},
		preview() {},
		setObjectValueReduce(obj, strKeys, data) {
			const arr = strKeys.split('.')
			arr.reduce((pre, item, i) => {
				if (arr.length === i + 1) {
					pre[item] = data
				} else if (!isObjectObject(pre[item])) {
					pre[item] = {}
				}
				return pre[item]
			}, obj)
		},
		setRespData(component, resp) {
			const { dataPath, renderKey, dataConsumer } = component.__config__
			if (!dataPath || !dataConsumer) return
			const respData = dataPath
				.split('.')
				.reduce((pre, item) => pre[item], resp)

			// 将请求回来的数据，赋值到指定属性。
			// 以el-tabel为例，根据Element文档，应该将数据赋值给el-tabel的data属性，所以dataConsumer的值应为'data';
			// 此时赋值代码可写成 component[dataConsumer] = respData；
			// 但为支持更深层级的赋值（如：dataConsumer的值为'options.data'）,使用setObjectValueReduce
			this.setObjectValueReduce(component, dataConsumer, respData)
			const i = this.drawingList.findIndex(
				item => item.__config__.renderKey === renderKey,
			)
			if (i > -1) this.$set(this.drawingList, i, component)
		},
		fetchData(component) {
			const { dataType, method, url } = component.__config__
			if (dataType === 'dynamic' && method && url) {
				this.setLoading(component, true)
				this.$axios({
					method,
					url,
				}).then(resp => {
					this.setLoading(component, false)
					this.setRespData(component, resp.data)
				})
			}
		},
		setLoading(component, val) {
			const { directives } = component
			if (Array.isArray(directives)) {
				const t = directives.find(d => d.name === 'loading')
				if (t) t.value = val
			}
		},
		activeFormItem(currentItem) {
			this.activeData = currentItem
			this.activeId = currentItem.__config__.formId
		},
		onEnd(obj) {
			if (obj.from !== obj.to) {
				this.fetchData(tempActiveData)
				this.activeData = tempActiveData
				this.activeId = this.idGlobal
			}
		},
		addComponent(item) {
			const clone = this.cloneComponent(item)
			this.fetchData(clone)
			this.drawingList.push(clone)
			this.activeFormItem(clone)
		},
		cloneComponent(origin) {
			const clone = deepClone(origin)
			const config = clone.__config__
			config.span = this.formConf.span // 生成代码时，会根据span做精简判断
			this.createIdAndKey(clone)
			clone.placeholder !== undefined && (clone.placeholder += config.label)
			tempActiveData = clone
			return tempActiveData
		},
		createIdAndKey(item) {
			const config = item.__config__
			config.formId = ++this.idGlobal
			config.renderKey = `${config.formId}${+new Date()}` // 改变renderKey后可以实现强制更新组件
			if (config.layout === 'colFormItem') {
				item.__vModel__ = `field${this.idGlobal}`
			} else if (config.layout === 'rowFormItem') {
				config.componentName = `row${this.idGlobal}`
				!Array.isArray(config.children) && (config.children = [])
				delete config.label // rowFormItem无需配置label属性
			}
			if (Array.isArray(config.children)) {
				config.children = config.children.map(childItem =>
					this.createIdAndKey(childItem),
				)
			}
			return item
		},
		AssembleFormData() {
			this.formData = {
				fields: deepClone(this.drawingList),
				...this.formConf,
			}
		},
		generate(data) {
			const func = this[`exec${titleCase(this.operationType)}`]
			this.generateConf = data
			func && func(data)
		},
		execRun() {
			this.AssembleFormData()
			this.drawerVisible = true
		},
		execDownload(data) {
			const codeStr = this.generateCode()
			const blob = new Blob([codeStr], { type: 'text/plain;charset=utf-8' })
			saveAs(blob, data.fileName)
		},
		execCopy() {
			document.getElementById('copyNode').click()
		},
		empty() {
			this.$confirm('确定要清空所有组件吗？', '提示', { type: 'warning' }).then(
				() => {
					this.drawingList = []
					this.idGlobal = 100
				},
			)
		},
		drawingItemCopy(item, list) {
			let clone = deepClone(item)
			clone = this.createIdAndKey(clone)
			list.push(clone)
			this.activeFormItem(clone)
		},
		drawingItemDelete(index, list) {
			list.splice(index, 1)
			this.$nextTick(() => {
				const len = this.drawingList.length
				if (len) {
					this.activeFormItem(this.drawingList[len - 1])
				}
			})
		},
		generateCode(isBeauti) {
			const { type } = this.generateConf
			this.AssembleFormData()
			const script = vueScript(
				makeUpJs(this.formData, type),
				this.formData,
				type,
			)
			const html = vueTemplate(makeUpHtml(this.formData, type), type)
			const css = cssStyle(makeUpCss(this.formData), type)
			let htmlDom
			if (type === 'html') {
				htmlDom = vueToHtml(script, css, html)
			} else if (type === 'vue') {
				htmlDom = html + script + css
			}
			// console.log(htmlDom, 111);
			if (isBeauti) {
				return htmlDom.replace(/\s/g, '')
			} else {
				return beautifier.html(htmlDom, beautifierConf.html)
			}
		},
		showJson() {
			this.AssembleFormData()
			this.jsonDrawerVisible = true
		},
		download() {
			if (this.formConf.formUrl) {
				this.dialogVisible = true
				this.showFileName = true
				this.operationType = 'download'
			} else {
				this.$message.error('请填写表单提交地址')
			}
		},
		run() {
			// this.dialogVisible = true
			const data = {
				fileName: '',
				type: 'vue',
			}
			this.showFileName = false
			this.operationType = 'run'
			const func = this[`exec${titleCase(this.operationType)}`]
			this.generateConf = data
			func && func(data)
		},
		copy() {
			this.dialogVisible = true
			this.showFileName = false
			this.operationType = 'copy'
		},
		tagChange(newTag) {
			newTag = this.cloneComponent(newTag)
			const config = newTag.__config__
			newTag.__vModel__ = this.activeData.__vModel__
			config.formId = this.activeId
			config.span = this.activeData.__config__.span
			this.activeData.__config__.tag = config.tag
			this.activeData.__config__.tagIcon = config.tagIcon
			this.activeData.__config__.document = config.document
			if (
				typeof this.activeData.__config__.defaultValue ===
				typeof config.defaultValue
			) {
				config.defaultValue = this.activeData.__config__.defaultValue
			}
			Object.keys(newTag).forEach(key => {
				if (this.activeData[key] !== undefined) {
					newTag[key] = this.activeData[key]
				}
			})
			this.activeData = newTag
			this.updateDrawingList(newTag, this.drawingList)
		},
		updateDrawingList(newTag, list) {
			const index = list.findIndex(
				item => item.__config__.formId === this.activeId,
			)
			if (index > -1) {
				list.splice(index, 1, newTag)
			} else {
				list.forEach(item => {
					if (Array.isArray(item.__config__.children)) {
						this.updateDrawingList(newTag, item.__config__.children)
					}
				})
			}
		},
		refreshJson(data) {
			this.drawingList = deepClone(data.fields)
			delete data.fields
			this.formConf = data
		},
	},
}
</script>

<style lang="scss">
@import './styles/index.scss';
@import './styles/home';
</style>
